#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of_platform.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/leds_pwm.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
//#include <asm/mach/map.h>


#define DEVICE_NAME				"pwm"
#define MISCLED_MINOR       144         	/* 子设备号 		*/
#define PWM_IOCTL_SET_FREQ		1
#define PWM_IOCTL_STOP			0

#define NS_IN_1HZ				(1000000000UL)
#define NS_DUTY                 (100000000UL)


struct pwm_device	*pwm_demo;  //定义pwm设备结构体

static void pwm_set_freq(unsigned long freq) 
{
    unsigned long duty_ns=0;    
   	duty_ns = NS_DUTY*freq;

	pwm_config(pwm_demo, duty_ns, NS_IN_1HZ);
	pwm_enable(pwm_demo);
}

static void pwm_stop(void) 
{
	pwm_config(pwm_demo, 0, NS_IN_1HZ);
	pwm_disable(pwm_demo);
}

static int rk3568_pwm_open(struct inode *inode, struct file *file)
{
	return 0;
}

static int rk3568_pwm_close(struct inode *inode, struct file *file) 
{
	return 0;
}

static long rk3568_pwm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
	switch (cmd) {
		case PWM_IOCTL_SET_FREQ:
			if (arg > 10)
				return -EINVAL;
			pwm_set_freq(arg);
			break;
		case PWM_IOCTL_STOP:
		default:
			pwm_stop();
			break;
	}

	return 0;
}

static struct file_operations rk3568_pwm_ops = {
	.owner			= THIS_MODULE,
	.open			= rk3568_pwm_open,
	.release		= rk3568_pwm_close, 
	.unlocked_ioctl	= rk3568_pwm_ioctl,
};

static struct miscdevice rk3568_misc_dev = {
	.minor = MISCLED_MINOR,
	.name = DEVICE_NAME,
	.fops = &rk3568_pwm_ops,
};

/*简单驱动*/
static int pwm_demo_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct device *dev = &pdev->dev;
	struct device_node *child; // 保存子节点
	printk("match success \n");

	child = of_get_next_child(dev->of_node, NULL);
	if (child)
	{
		pwm_demo = devm_of_pwm_get(dev, child, NULL);
		if (IS_ERR(pwm_demo)) 
		{
			printk(KERN_ERR" pwm_demo，get pwm error!!\n");
			return -1;
		}
	}
	else
	{
		printk(KERN_ERR" pwm_demoof_get_next_child error!!\n");
		return -1;
	}

	/*配置频率100KHz 占空比80%*/
	//pwm_config(pwm_demo, 2500, 5000);
	/* 频率100KHz 占空比20%*/
	pwm_set_polarity(pwm_demo, PWM_POLARITY_NORMAL);
    printk("%s %d\r\n",__func__, __LINE__);
	pwm_enable(pwm_demo);

    misc_register(&rk3568_misc_dev);

	return ret;
}

static int pwm_demo_remove(struct platform_device *pdev)
{
    printk("%s %d\r\n",__func__, __LINE__);
	// pwm_config(pwm_demo, 0, 50000);
    pwm_stop();
    misc_deregister(&rk3568_misc_dev);
	return 0;
}

static const struct of_device_id of_pwm_leds_match[] = {
	{.compatible = "pwm_demo",},
	{},
};

static struct platform_driver pwm_demo_driver = {
	.probe		= pwm_demo_probe,
	.remove		= pwm_demo_remove,
	.driver		= {
		.name	= "pwm_demo",
		.of_match_table = of_pwm_leds_match,
	},
};

module_platform_driver(pwm_demo_driver);

MODULE_DESCRIPTION(" Embedfire PWM DEMO driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZOROE");
MODULE_INFO(intree, "Y");


